#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
#include <map>
#include <unordered_map>
#include <jsoncpp/json/json.h>

#include "log.hpp"
extern Log log;

const std::string allow_path = "/home/qyc/initial---linux/lesson_design/embed_competition/wwwroot";
const std::string wwwroot = "./wwwroot";
const std::string home_page = "index.html";

class HttpRequest
{
private:
    std::string url_decode(const std::string& encoded)
    {
        std::string decoded;
        for (size_t i = 0; i < encoded.length(); ++i)
        {
            if (encoded[i] == '+')
                decoded += ' ';
            else if (encoded[i] == '%' && i + 2 < encoded.length())
            {
                int hexValue = 0;
                sscanf(encoded.substr(i + 1, 2).c_str(), "%x", &hexValue);
                decoded += static_cast<char>(hexValue);
                i += 2;
            } 
            else
                decoded += encoded[i];
        }
#ifdef __TEST_DEBUG__
        log(Log::Debug, "urlDecode sucess, url: %s", decoded.c_str());
#endif
        return decoded;
    }

    void get_parameter(std::string& code, std::multimap<std::string, std::string>& param)
    {
        param.clear();
        size_t pos = 0;
        const size_t len = code.length();
        while (pos < len)
        {
            // 查找键的结束位置（=号）
            size_t eq_pos = code.find('=', pos);
            
            // 处理没有=号的情况
            std::string key;
            if (eq_pos == std::string::npos)
            {
                key = code.substr(pos);
                param.insert({key, ""});
                break;
            }
            
            // 提取键
            key = code.substr(pos, eq_pos - pos);
            pos = eq_pos + 1;
            
            // 查找值的结束位置
            size_t amp_pos = code.find('&', pos);
            std::string value;
            
            if (amp_pos == std::string::npos)
            {
                value = code.substr(pos);
                pos = len;
            }
            else
            {
                value = code.substr(pos, amp_pos - pos);
                pos = amp_pos + 1;
            }
            
            // 插入参数
            param.insert({key, value});
        }
#ifdef __TEST_DEBUG__
        log(Log::Debug, "get parameter success");
#endif
    }

    void detach_url_param()
    {
        size_t pos = _url.find('?');
        if (pos == std::string::npos)
            return;
        std::string query = _url.substr(pos + 1);
        _url.erase(pos);
        get_parameter(query, _param);
#ifdef __TEST_DEBUG__
        log(Log::Debug, "detach url param success");
#endif
    }
    
    void header_handle()
    {
        std::stringstream ss(_header[0]);
        ss >> _method >> _url >> _http_version;
        _url = url_decode(_url);
        detach_url_param();

        for (size_t i = 1; i < _header.size(); ++i)
        {
            if (strncmp(_header[i].c_str(), "Content-Type: ", strlen("Content-Type: ")) == 0)
                _content_type = _header[i].substr(strlen("Content-Type: "));
            if (strncmp(_header[i].c_str(), "Content-Length: ", strlen("Content-Length: ")) == 0)
                _content_length = std::stoi(_header[i].substr(strlen("Content-Length: ")));
        }
    }

    void method_handle()
    {
        if (_url.empty())
        {
            _file_path = wwwroot + "./err.html";
            return;
        }
        if (_url.find("/api/") == std::string::npos)
        {
            _file_path = wwwroot;
            if (_url == "/" || _url == "/index.html")
            {
                _file_path += '/';
                _file_path += home_page;
            }
            else
                _file_path += _url;
            size_t pos = _file_path.rfind(".");
            if (pos == std::string::npos)
                _suffix = ".html";
            else
                _suffix = _file_path.substr(pos);
            log(Log::Info, "get data sucess, data: %s", _file_path.c_str());
        }
        else
        {
            _api = _url.substr(_url.find("/api/") + strlen("/api/"));
            log(Log::Info, "get api sucess, api: %s", _api.c_str());
        }
    }
    void content_type_handle()
    {
        if (_content_type == "application/x-www-form-urlencoded")
        {
            _body = url_decode(_body);
            std::stringstream ss(_body);
            std::string line;
            std::getline(ss, line);
            get_parameter(line, _param);
        }
        else if (_content_type == "application/json")
        {
            parse_json_to_multimap(_body, _param);
#ifdef __TEST_DEBUG__
            for (auto e : _param)
                std::cout << e.first << ":" << e.second << ' ';
            std::cout << std::endl;
#endif
        }
    }

    void json_to_multimap(const Json::Value& root, const std::string& prefix, std::multimap<std::string, std::string>& param)
    {   
        if (root.isObject())
        {
            // 对象类型：遍历所有成员
            Json::Value::Members members = root.getMemberNames();
            for (const std::string& key : members)
            {
                std::string new_prefix = prefix.empty() ? key : prefix + "." + key;
                json_to_multimap(root[key], new_prefix, param);
            }
        }
        else if (root.isArray())
        {
            // 数组类型：遍历所有元素
            for (Json::ArrayIndex i = 0; i < root.size(); ++i)
            {
                std::string new_prefix = prefix + "[" + std::to_string(i) + "]";
                json_to_multimap(root[i], new_prefix, param);
            }
        }
        else if (root.isString())
            param.insert({prefix, root.asString()});
        else if (root.isNumeric())
            param.insert({prefix, std::to_string(root.asDouble())});
        else if (root.isBool())
            param.insert({prefix, root.asBool() ? "true" : "false"});
        else if (root.isNull())
            param.insert({prefix, "null"});
        else
            param.insert({prefix, root.toStyledString()});
    }
    void parse_json_to_multimap(const std::string& json_str, std::multimap<std::string, std::string>& param)
    {
        Json::Reader reader;
        Json::Value root;
        
        if (!reader.parse(json_str, root))
        {
            log(Log::Warning, "parse json error, errstring: %s", reader.getFormattedErrorMessages().c_str());
            return;
        }
        
        std::string prefix;
        return json_to_multimap(root, prefix, param);
    }
    void parse()
    {
        method_handle();
        content_type_handle();
    }
public:
    bool deserializer(std::string& req)
    {
        size_t methodPos = req.find("GET ");
        if (methodPos == std::string::npos)
        {
            methodPos = req.find("POST ");
            if (methodPos == std::string::npos)
            {
#ifdef __TEST_DEBUG__
                log(Log::Info, "invalid http request");
#endif                
                req.clear();
                return false;
            }
        }
        if (methodPos > 0)
            req.erase(0, methodPos);
        
        size_t header_end = req.find("\r\n\r\n");
        if (header_end == std::string::npos)
        {
#ifdef __TEST_DEBUG__
            log(Log::Debug, "http request not enought");
#endif
            return false;
        }
        // 解析头部
        std::istringstream headerStream(req.substr(0, header_end));
        std::string line;
        _header.clear();
        
        if (std::getline(headerStream, line) && !line.empty())
        {
            if (line.back() == '\r')
                line.pop_back();
            _header.push_back(line);
            std::istringstream startLine(line);
            startLine >> _method >> _url >> _http_version;
            _url = url_decode(_url);
            detach_url_param();
        }
        else
        {
#ifdef __TEST_DEBUG__
            log(Log::Debug, "http request not enought");
#endif
            return false;
        }
        
        // 解析头部字段
        _content_length = 0;
        _content_type.clear();
        
        while (std::getline(headerStream, line) && !line.empty())
        {
            if (line.back() == '\r') line.pop_back();
            _header.push_back(line);
            
            // 解析Content-Type和Content-Length
            if (line.compare(0, strlen("Content-Type:"), "Content-Type:") == 0)
            {
                _content_type = line.substr(strlen("Content-Type:"));
                _content_type.erase(0, _content_type.find_first_not_of(" \t"));
            } 
            else if (line.compare(0, strlen("Content-Length:"), "Content-Length:") == 0)
            {
                std::string lenStr = line.substr(strlen("Content-Length:"));
                lenStr.erase(0, lenStr.find_first_not_of(" \t"));
                try
                {
                    _content_length = std::stoul(lenStr);
                }
                catch (...)
                {
                    _content_length = 0;
                }
            }
        }
        
        // 检查是否有完整的body
        size_t body_begin = header_end + 4;
        if (req.size() < body_begin + _content_length)
        {
#ifdef __TEST_DEBUG__
            log(Log::Debug, "http request not enought");
#endif
            return false;
        }
        // 提取body
        _body = req.substr(body_begin, _content_length);
        
        // 从请求中移除已处理的数据
        req.erase(0, body_begin + _content_length);
        
        // 处理解析后的数据
        parse();
        
        return true;
    }
#ifdef __TEST_DEBUG__
    void print()
    {
        std::cout << "********************" << std::endl;
        std::cout << "method: " << _method << " url: " << _url << " version: " << _http_version << std::endl;
        if (_method == "GET")
            std::cout << "file path: " << _file_path << " suffix: " << _suffix << std::endl;
        else if (_method == "POST")
            std::cout << "api: " << _api << std::endl;
        if (!_param.empty())
        {
            std::cout << "param: ";
            for (auto e : _param)
                std::cout << e.first << ':' << e.second << ' ';
            std::cout << std::endl;
        }
        if (!_content_type.empty())
            std::cout << "content_type: " << _content_type << ' ' << "content_length: " << _content_length << std::endl;
        for (size_t i = 0; i < _header.size(); ++i)
            std::cout << _header[i] << std::endl;
        
        std::cout << "--------body--------"<< std::endl;
        std::cout << _body << std::endl;
        std::cout << "####################" << std::endl;
    }
#endif
    std::string serializer()
    {
        std::string req;
        for (size_t i = 0; i < _header.size(); ++i)
        {
            req += _header[i];
            req += "\r\n";
        }
        req += "\r\n";
        req += _body;
    }
public:
    std::vector<std::string> _header;

    std::string _method;
    std::string _url;
    std::string _api;
    std::multimap<std::string, std::string> _param;
    std::string _http_version;
    std::string _content_type;
    size_t _content_length = 0;
    
    std::string _file_path; // 请求的文件路径
    std::string _suffix;    // 文件后缀

    std::string _body;
};

class HttpResponse
{
private:
    std::string suffix_to_desc(const std::string& suffix)
    {
        static class ContentType
        {
        public:
            ContentType()
            {
                _types.insert({".html", "text/html"});
                _types.insert({".jpg", "image/jpeg"});
                _types.insert({".ico", "application/x-ico"});
                _types.insert({".txt", "text/plain"});
                _types.insert({".css", "text/css"});
                _types.insert({".exe", "application/x-msdownload"});
                _types.insert({".json", "application/json"});
            }
            std::unordered_map<std::string, std::string> _types;
        } content_type;
        auto iter = content_type._types.find(suffix);
        if (iter == content_type._types.end())
            return content_type._types[".html"];
        else
            return iter->second;
    }
public:
    static std::string read_file(const std::string& path)
    {
        std::ifstream fin(path, std::ios::binary | std::ios::ate);
        if (!fin.is_open())
            return "";
        
        const auto len = fin.tellg();
        if (len <= 0)
            return "";
        
        const auto file_size = static_cast<std::size_t>(len);
        fin.seekg(0, std::ios::beg);

        std::string content;
        content.resize(file_size);
        
        if (!fin.read(&content[0], file_size))
            return "";
        return content;
    }

    void serializer(const HttpRequest& req)
    {
        bool ok = true;
        std::string text = read_file(req._file_path);
        if (text.empty())
            ok = false;
        else
        {
            const char* absolute_path = realpath(req._file_path.c_str(), nullptr);
            if (strncmp(allow_path.c_str(), absolute_path, allow_path.size()) != 0)
                ok = false;
#ifdef __TEST_DEBUG__
            log(Log::Debug, "absolute_path: %s", absolute_path);
#endif
        }

        std::string response_line;
        if (ok)
            response_line = "HTTP/1.0 200 OK\r\n";
        else
        {
            response_line = "HTTP/1.0 404 Not Found";
            text = read_file("./wwwroot/err.html");
        }
        
        std::string response_header = "Content-Length: ";
        response_header += std::to_string(text.size());
        response_header += "\r\n";
        response_header += "Content-Type: ";
        response_header += suffix_to_desc(ok ? req._suffix : ".html");
        response_header += "\r\n";
        const std::string blank_line = "\r\n";

        response = response_line;
        response += response_header;
        response += blank_line;
        response += text;
    }
    
    void serializer(const std::string& message, const std::string& suffix)
    {
        std::string response_line = "HTTP/1.0 200 OK\r\n";
        std::string response_header = "Content-Length: ";
        response_header += std::to_string(message.size());
        response_header += "\r\n";
        response_header += "Content-Type: ";
        response_header += suffix_to_desc(suffix);
        response_header += "\r\n";
        const std::string blank_line = "\r\n";

        response = response_line;
        response += response_header;
        response += blank_line;
        response += message;
    }
public:
    std::string response;
};